/* 
 * Programme simulant la gestion d'une imprimante
 * 
 * Code grandement inspiré de :
 * http://kurzweg.info/cnam/smb137/partie6/ar01s01.html
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>

#define MAXNBTHREADS 3
#define MAXNBIMPRESSIONS 5

struct sbuf_t{
    pthread_t threads[MAXNBTHREADS];
    char buf[MAXNBIMPRESSIONS][80];   /* shared var */
    int in;         	  /* buf[in%BUFF_SIZE] is the first empty slot */
    int out;        	  /* buf[out%BUFF_SIZE] is the first full slot */
    sem_t full;     	  /* keep track of the number of full spots */
    sem_t empty;    	  /* keep track of the number of empty spots */
    pthread_mutex_t printmutex;    	  /* enforce mutual exclusion to shared data */
    pthread_mutex_t mutex;
};

struct sbuf_t shared;

void *impression(void *arg){
  char *nomFic = (char *) arg;
  int rc;
  /* Lock mutex */
  rc = pthread_mutex_lock(&shared.printmutex);
  if(rc) {
    error(EXIT_FAILURE, rc, "pthread_mutex_lock");
  }
  printf("Debut d'impression de \"%s\" par %i...\n", nomFic,(long int)syscall(224)); /* Why gettid() doesn't work ? gettid */
  sleep(5); /* Simule la lenteur de l'impression */
  printf("...Fin d'impression de \"%s\"...\n", nomFic);
  /* Unlock */
  rc = pthread_mutex_unlock(&shared.printmutex);
  if(rc) {
    error(EXIT_FAILURE, rc, "pthread_mutex_lock");
  }
}

void *attenteImpression(void *arg){
  do {
    /* Attente d'un objet */
    sem_wait(&shared.full);
    printf("There is an object to print...\n");
    /* Mutex */
    pthread_mutex_lock(&shared.mutex);
    printf("Mutex attenteImpression lock\n");
    /* lancement de l'impression de l'objet */
    impression(shared.buf[shared.in]);
    //printf("Impression...\n");
    /* Décrément de l'emplacement à lire */
    shared.in = (shared.in + 1) % MAXNBIMPRESSIONS;
    /* Unlock mutex */
    pthread_mutex_unlock(&shared.mutex);
    printf("Mutex attenteImpression unlock\n");
    /* Signaler un emplacement libre */
    sem_post(&shared.empty);
  } while (1);
}
void attenteEcriture() {
  char nomFic[80];
  do {
    printf("Nom du fichier a imprimer (taper '0' pour terminer) ?\n");
    scanf("%s", nomFic);
    /* Il y a t il un emplacement de libre ?  */
    sem_wait(&shared.empty);
    printf("Semaphore passed (attenteEcriture) \n");
    /* Il faudrait peut être placer un mutex */
    pthread_mutex_lock(&shared.mutex);
    printf("Mutex attenteEcriture lock !\n");
    /* Depose de l'objet */
    strcpy(shared.buf[shared.out],nomFic);
    /* Incrément de IN */
    shared.out = (shared.out + 1) % MAXNBIMPRESSIONS;
    /* ?? Libération du mutex ?? */
    pthread_mutex_unlock(&shared.mutex);
    printf("Mutex attenteEcriture unlock\n");
    /* Signaler un emplacement occupé */
    sem_post(&shared.full);
  }while (1);
}
int main(){
  int i, rc;
  /* Init shared */
  shared.in = 0;
  shared.out = 0;
  /* Initialisation des sémaphores */
  sem_init(&shared.empty, 0, MAXNBIMPRESSIONS);
  sem_init(&shared.full, 0, 0);
  /* Creation mutex */
  pthread_mutex_init(&shared.printmutex, NULL);  /* Init mutex */
  pthread_mutex_init(&shared.mutex, NULL);  /* Init mutex */
  /* Creation des threads */
  for (i=0; i <= MAXNBTHREADS; i++) {
    rc = pthread_create(&shared.threads[i], NULL, attenteImpression, NULL);
    if(rc) {
      error(EXIT_FAILURE, rc, "pthread_create");
    }
    /*Detachement */
    if (pthread_detach(shared.threads[i]) != 0) {
      error(EXIT_FAILURE, rc, "pthread_detach");
    }
  }
  /* Lancement de la fonction d'attente des écritures */
  attenteEcriture();
  rc = pthread_mutex_destroy(&shared.mutex);
  rc = pthread_mutex_destroy(&shared.printmutex); 
  if (rc) {
    error(EXIT_FAILURE, rc, "pthread_mutex_destroy");
  }
  return EXIT_SUCCESS;
}
